package com.thunder.ktv.scoreengine2test.ui; // 请替换为你的包名

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Choreographer;
import android.view.View;
import android.widget.Toast;

import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;

public class SpectrumView2 extends View implements Choreographer.FrameCallback {
    private static final String TAG = "SpectrumViewHeatmap"; // 日志标签

    private Paint spectrumPaint; // 用于绘制频谱的画笔
    private Paint bitmapPaint;   // 用于绘制位图的画笔

    private Bitmap offscreenBitmap; // 离屏缓冲位图，用于双缓冲绘图
    private Canvas offscreenCanvas; // 用于在离屏位图上绘制的画布

    // --- 数据和配置 ---
    private float[] pendingSpectrumData;    // 等待处理的新频谱数据
    private final Object dataLock = new Object(); // 用于同步访问 pendingSpectrumData 的锁对象
    private boolean hasPendingData = false;     // 标记是否有等待处理的数据

    private LinkedList<float[]> spectrumHistory; // 存储原始频谱数据的历史记录 (-127 到 127)
    private int maxHistorySizeBasedOnWidth = 200; // 基于视图宽度的历史记录最大条数

    private int spectrumBarWidth = 1;           // 频谱条（热力图中的列）的宽度，默认为1像素
    private final int SPECTRUM_DATA_SIZE = 256; // 固定的频谱数据点数量
    private int viewBackgroundColor = Color.BLACK; // 视图的背景颜色

    // --- Choreographer 相关 ---
    private Choreographer choreographer;          // 用于同步绘制操作到显示刷新周期
    private boolean frameCallbackRegistered = false; // 标记是否已注册帧回调

    // --- 绘图辅助对象 ---
    private Rect srcRect = new Rect(); // 源矩形，用于位图拷贝
    private Rect dstRect = new Rect(); // 目标矩形，用于位图拷贝

    // --- 增强处理参数 (参考 Python 示例) ---

    // ENHANCE_POWER 参数在 enhanceColumn 方法中用于对频谱数据进行幂次变换（Gamma 校正或幂律变换），
    // 其主要作用是调整图像的对比度和亮度，特别是改变低能量和高能量区域的相对视觉权重。
    // 值 < 1 会提升较暗区域的亮度，使细节更明显；值 > 1 会增加对比度，使亮区更亮，暗区更暗。
    private static final float ENHANCE_POWER = 0.5f; // 幂次增强参数

    /*
     ENHANCE_PERCENTILE 参数在 enhanceColumn 方法中用于动态调整频谱数据的对比度，
     使得大部分信号能够更好地在颜色渐变中展现出来，而不是被少数极端的高能量值“压制”。
     它定义了一个百分位数值，用于确定归一化范围的上限。
     例如，99.0f 表示将数据中从小到大排列后第99%位置的值作为动态范围的最大参考值之一。
     */
    private static final float ENHANCE_PERCENTILE = 99.0f; // 百分位参数，用于动态范围调整

    // ENHANCE_THRESHOLD 是一个阈值，用于过滤掉非常低的能量值，或者作为数据归一化的基准下限。
    // 输入数据范围是 -127 到 127。
    // - Python 示例中的 -20dB 是基于 20*log10(0..1线性值) 计算的，所以值较低。
    // - 如果 -127 代表静音，0 代表一个不错的信号，127 代表峰值，
    //   那么这个阈值可能设置在 -80 到 -50 之间比较合适（以输入数据的原始值为准）。
    //   当前设置为 0，意味着能量低于0的值都会被当作0处理（在第一步阈值处理后）。这是一个可调参数。
    private static final float ENHANCE_THRESHOLD = -0f; // 能量阈值参数

    // --- 颜色渐变 (黑 -> 深蓝 -> 亮蓝 -> 青色) ---
    private int[] lut; // 颜色查找表 (Lookup Table)
    private static final int LUT_SIZE = 256; // 查找表的大小，对应归一化后的能量值

    // 定义颜色渐变的颜色锚点 (ARGB格式)
    private static final int[] GRADIENT_COLORS = new int[]{
            Color.rgb(0, 0, 0),        // 黑色
            Color.rgb(0, 0, 128),      // 深蓝色
            Color.rgb(0, 128, 255),    // 亮蓝色
            Color.rgb(0, 255, 255)     // 青色
    };
    // 定义颜色锚点在渐变中的位置 (0.0 到 1.0)
    private static final float[] GRADIENT_POSITIONS = new float[]{
            0.0f,   // 对应 GRADIENT_COLORS[0]
            0.33f,  // 对应 GRADIENT_COLORS[1]
            0.66f,  // 对应 GRADIENT_COLORS[2]
            1.0f    // 对应 GRADIENT_COLORS[3]
    };



    // --- 新增：用于绘制“已暂停”状态的成员变量 ---
    private Paint pausedTextPaint; // 绘制提示文字的画笔
    private Rect textBounds = new Rect(); // 用于测量文字边界，以实现精确居中
    private static final String PAUSED_TEXT = "已暂停显示,点击打开显示"; // 提示文字内容

    private Paint infoTextPaint;    // 绘制信息文本的画笔
    private String infoText;        // 要显示的信息文本
    private float infoTextMargin;   // 信息文本的边距

    public SpectrumView2(Context context) {
        super(context);
        init(null);
    }

    public SpectrumView2(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public SpectrumView2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private void init(@Nullable AttributeSet attrs) {
        spectrumPaint = new Paint();
        spectrumPaint.setStyle(Paint.Style.FILL); // 设置画笔样式为填充
        spectrumPaint.setAntiAlias(false);       // 关闭抗锯齿，热力图通常不需要

        bitmapPaint = new Paint();
        bitmapPaint.setFilterBitmap(false);      // 关闭位图滤波，保持像素锐利

        spectrumHistory = new LinkedList<>();
        choreographer = Choreographer.getInstance(); // 获取 Choreographer 实例

        initializeLut(); // 初始化颜色查找表
        setSpectrumBarWidth(1); // 确保默认频谱条宽度为1，以实现热力图效果

        // --- 新增：初始化“已暂停”状态的画笔 ---
        pausedTextPaint = new Paint();
        pausedTextPaint.setColor(Color.DKGRAY); // 设置文字颜色为深灰色
        // 使用 sp 单位设置字体大小，使其在不同分辨率设备上表现更一致
        float textSizeInPx = TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()
        );
        pausedTextPaint.setTextSize(textSizeInPx);
        pausedTextPaint.setAntiAlias(true);
        pausedTextPaint.setTextAlign(Paint.Align.CENTER); // 设置文本对齐方式为居中，方便水平居中

        infoTextPaint = new Paint();
        infoTextPaint.setColor(Color.WHITE); // 默认白色文字
        infoTextPaint.setAntiAlias(true);
        // 使用 sp 单位设置字体大小
        float infoTextSizeInPx = TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics()
        );
        infoTextPaint.setTextSize(infoTextSizeInPx);
        // 设置阴影以提高在复杂背景下的可读性
        infoTextPaint.setShadowLayer(5.0f, 2.0f, 2.0f, Color.BLACK);

        // 使用 dp 单位设置边距
        infoTextMargin = TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics()
        );

        // --- 新增：从 XML 属性中读取 tag ---
        if (attrs != null) {
            // 获取 android:tag 属性的值
            infoText = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "tag");
        }

        // --- 修改：更新点击事件监听器逻辑 ---
        setOnClickListener(view -> {
            show = !show; // 切换显示状态
            if (!show) {
                // 如果从显示切换到不显示，则清空数据
                clear();
            }
            // 无论切换到哪种状态，都必须调用 invalidate() 来触发 onDraw，
            // 以便立即更新UI（显示“已暂停”或显示空的频谱背景）
            // clear() 方法内部已经调用了 invalidate()，所以这里只需要处理 else 的情况
            else {
                invalidate();
            }
        });
    }

    // 初始化颜色查找表
    private void initializeLut() {
        lut = new int[LUT_SIZE];
        for (int i = 0; i < LUT_SIZE; i++) {
            float normalizedValue = (float) i / (LUT_SIZE - 1); // 将索引归一化到 0.0 - 1.0
            lut[i] = getColorFromGradientPosition(normalizedValue); // 根据归一化值获取渐变颜色
        }
    }

    // 根据给定的位置 (0.0-1.0) 从颜色渐变中获取颜色
    private int getColorFromGradientPosition(float position) {
        if (position <= GRADIENT_POSITIONS[0]) { // 小于等于第一个锚点位置
            return GRADIENT_COLORS[0];
        }
        if (position >= GRADIENT_POSITIONS[GRADIENT_POSITIONS.length - 1]) { // 大于等于最后一个锚点位置
            return GRADIENT_COLORS[GRADIENT_COLORS.length - 1];
        }

        // 在两个锚点之间进行线性插值
        for (int i = 0; i < GRADIENT_POSITIONS.length - 1; i++) {
            if (position >= GRADIENT_POSITIONS[i] && position <= GRADIENT_POSITIONS[i + 1]) {
                float t = (position - GRADIENT_POSITIONS[i]) / (GRADIENT_POSITIONS[i + 1] - GRADIENT_POSITIONS[i]); // 计算插值因子
                int c1 = GRADIENT_COLORS[i];     // 起始颜色
                int c2 = GRADIENT_COLORS[i + 1]; // 结束颜色

                // 对 RGB 分量进行线性插值
                int r = (int) (Color.red(c1) * (1 - t) + Color.red(c2) * t);
                int g = (int) (Color.green(c1) * (1 - t) + Color.green(c2) * t);
                int b = (int) (Color.blue(c1) * (1 - t) + Color.blue(c2) * t);
                return Color.rgb(r, g, b);
            }
        }
        return GRADIENT_COLORS[GRADIENT_COLORS.length - 1]; // 理论上不会执行到这里，作为备用返回最后一个颜色
    }
    boolean show = true;

    // 外部调用此方法更新频谱数据
    public void updateSpectrum(float[] newSpectrum) {
        if(!show){
            return;
        }
        float[] dataCopy;
        if (newSpectrum == null || newSpectrum.length != SPECTRUM_DATA_SIZE) {
            dataCopy = new float[SPECTRUM_DATA_SIZE]; // 如果数据无效，使用全零数组
            // Arrays.fill(dataCopy, -127f); // 或者用一个代表静音的值填充，例如-127
        } else {
            dataCopy = newSpectrum.clone(); // 复制数组，避免外部修改影响
        }

        synchronized (dataLock) {
            pendingSpectrumData = dataCopy; // 存储待处理数据
            hasPendingData = true;         // 设置标记
        }

        // 如果帧回调未注册，则注册一个
        if (!frameCallbackRegistered) {
            frameCallbackRegistered = true;
            choreographer.postFrameCallback(this);
        }
    }

    @Override
    public void doFrame(long frameTimeNanos) { // Choreographer 帧回调方法
        frameCallbackRegistered = false; // 重置标记，以便下次 updateSpectrum 可以重新请求

        float[] rawDataToProcess = null; // 存储从 pendingSpectrumData 取出的原始数据 (-127 到 127)
        boolean processThisFrame = false; // 标记本帧是否需要处理数据

        synchronized (dataLock) {
            if (hasPendingData) {
                rawDataToProcess = pendingSpectrumData;
                pendingSpectrumData = null; // 消费数据
                hasPendingData = false;
                processThisFrame = true;
            }
        }

        if (processThisFrame && rawDataToProcess != null) {
            if (getWidth() > 0 && getHeight() > 0) { // 确保视图有有效尺寸
                if (ensureOffscreenBitmap()) { // 确保离屏位图已准备好
                    // 将原始数据添加到历史记录
                    spectrumHistory.addLast(rawDataToProcess);
                    trimSpectrumHistory(); // 整理历史记录，移除过旧的数据

                    // 对当前帧的原始数据进行增强处理
                    float[] enhancedData = enhanceColumn(rawDataToProcess);
                    // 使用增强后的数据更新离屏位图
                    updateBitmapWithEnhancedData(enhancedData);
                    invalidate(); // 请求重绘视图，将离屏位图绘制到屏幕
                }
            }
        }
    }

    // 使用增强后的频谱数据更新离屏位图
    private void updateBitmapWithEnhancedData(float[] enhancedSpectrumData) {
        // 1. 平移位图内容：将现有图像向左移动一个频谱条的宽度
        if (offscreenBitmap.getWidth() > spectrumBarWidth) {
            // 源矩形：从 (spectrumBarWidth, 0) 到 (bitmap.width, bitmap.height)
            srcRect.set(spectrumBarWidth, 0, offscreenBitmap.getWidth(), offscreenBitmap.getHeight());
            // 目标矩形：从 (0, 0) 到 (bitmap.width - spectrumBarWidth, bitmap.height)
            dstRect.set(0, 0, offscreenBitmap.getWidth() - spectrumBarWidth, offscreenBitmap.getHeight());
            offscreenCanvas.drawBitmap(offscreenBitmap, srcRect, dstRect, null); // null 画笔表示直接像素拷贝
        } else {
            // 如果位图宽度不足以平移，则用背景色清空
            offscreenCanvas.drawColor(viewBackgroundColor);
        }

        // 2. 在最右侧绘制新的频谱条（现在是热力图的一列彩色像素）
        float newBarX = Math.max(0, offscreenBitmap.getWidth() - spectrumBarWidth);
        // float actualBarWidthToDraw = offscreenBitmap.getWidth() - newBarX; // 理论上等于 spectrumBarWidth

        // (可选) 清理新条带区域：如果背景不是纯黑且颜色不完全不透明，这一步可能重要。
        // 但由于是逐像素绘制，通常会被新内容覆盖。
        // spectrumPaint.setColor(viewBackgroundColor);
        // offscreenCanvas.drawRect(newBarX, 0, offscreenBitmap.getWidth(), offscreenBitmap.getHeight(), spectrumPaint);

        // 使用增强后的数据绘制新的频谱列
        drawSpectrumColumnOnCanvas(offscreenCanvas, enhancedSpectrumData, newBarX, spectrumBarWidth,
                offscreenBitmap.getHeight(), SPECTRUM_DATA_SIZE);
    }

    // 对单列原始频谱数据进行增强处理
    private float[] enhanceColumn(float[] rawColumnData) {
        if (rawColumnData == null) return new float[SPECTRUM_DATA_SIZE]; // 防御空指针

        float[] processedData = new float[rawColumnData.length];

        // 步骤 1: 阈值处理
        // 将所有低于 ENHANCE_THRESHOLD 的值设置为 ENHANCE_THRESHOLD，并找出当前列的最大值。
        float currentMaxVal = -Float.MAX_VALUE; // 初始化为可能的最小值
        for (int i = 0; i < rawColumnData.length; i++) {
            processedData[i] = Math.max(rawColumnData[i], ENHANCE_THRESHOLD);
            if (processedData[i] > currentMaxVal) {
                currentMaxVal = processedData[i];
            }
        }

        // 步骤 2: 归一化到 0-1 范围
        // 基于应用阈值后的 currentMaxVal 和 ENHANCE_THRESHOLD 进行归一化。
        if (currentMaxVal > ENHANCE_THRESHOLD) {
            float range = currentMaxVal - ENHANCE_THRESHOLD;
            if (range > 0) { // 避免除以零或负数
                for (int i = 0; i < processedData.length; i++) {
                    processedData[i] = (processedData[i] - ENHANCE_THRESHOLD) / range;
                }
            } else { // 所有值都等于或低于阈值，或者只有一个值在阈值处
                for (int i = 0; i < processedData.length; i++) {
                    processedData[i] = 0; // 如果没有动态范围，则设为0
                }
            }
        } else { // 所有值都等于或低于阈值
            for (int i = 0; i < processedData.length; i++) {
                processedData[i] = 0;
            }
        }

        // 步骤 3: 幂次增强 (Gamma 校正)
        for (int i = 0; i < processedData.length; i++) {
            processedData[i] = (float) Math.pow(processedData[i], ENHANCE_POWER);
        }

        // 步骤 4: 动态范围调整 (基于百分位数)
        // 创建临时列表用于排序，以找到百分位数值。
        ArrayList<Float> sortedData = new ArrayList<>(processedData.length);
        for (float val : processedData) {
            sortedData.add(val);
        }
        Collections.sort(sortedData); // 排序

        float vMin = sortedData.get(0); // 排序后的最小值
        float vMaxPercentile;
        if (sortedData.isEmpty()){ // 理论上不会发生，因为 processedData 有元素
            vMaxPercentile = vMin;
        } else {
            // 计算 ENHANCE_PERCENTILE 对应的索引
            int percentileIndex = Math.min(sortedData.size() - 1, (int) (sortedData.size() * (ENHANCE_PERCENTILE / 100.0f)));
            vMaxPercentile = sortedData.get(percentileIndex); // 获取该百分位数的值
        }

        // 使用 vMin 和 vMaxPercentile 重新归一化数据
        if (vMaxPercentile > vMin) {
            float range = vMaxPercentile - vMin;
            for (int i = 0; i < processedData.length; i++) {
                processedData[i] = (processedData[i] - vMin) / range;
            }
        } else { // 如果幂次增强后没有动态范围 (例如，所有值都为0)
            for (int i = 0; i < processedData.length; i++) {
                // 如果 vMaxPercentile 等于 vMin，但大于0，则所有值都是该值。
                // 归一化 (data-vMin)/(vMax-vMin) 会导致 0/0。
                // 如果 vMaxPercentile == vMin，意味着达到该百分位数的所有值都相同。
                // 如果 vMin 已经是0，这符合预期。
                // 如果 vMin > 0 且 vMaxPercentile == vMin，则如果 data[i] == vMin，结果应为1.0，否则为0。
                processedData[i] = (processedData[i] > vMin) ? 1.0f : ( (vMin > 0 && processedData[i] == vMin) ? 1.0f : 0.0f);
            }
        }

        // 步骤 5: 将值裁剪到 0.0 - 1.0 范围
        for (int i = 0; i < processedData.length; i++) {
            processedData[i] = Math.max(0.0f, Math.min(1.0f, processedData[i]));
        }
        return processedData; // 返回处理后的数据 (0.0 - 1.0范围)
    }

    // 确保离屏位图存在且尺寸正确
    private boolean ensureOffscreenBitmap() {
        int width = getWidth();
        int height = getHeight();
        if (width <= 0 || height <= 0) { // 视图尺寸无效
            releaseOffscreenBitmap(); // 释放旧位图
            return false;
        }

        // 如果位图为空，或尺寸与视图不匹配，则重新创建
        if (offscreenBitmap == null || offscreenBitmap.getWidth() != width || offscreenBitmap.getHeight() != height) {
            releaseOffscreenBitmap();
            try {
                // 创建 ARGB_8888 格式的位图，支持透明度
                offscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                offscreenCanvas = new Canvas(offscreenBitmap); // 将画布与新位图关联
                Log.d(TAG, "创建离屏位图: " + width + "x" + height);
                redrawEntireBitmapFromHistory(); // 用历史数据重新填充新位图
            } catch (OutOfMemoryError e) {
                Log.e(TAG, "创建离屏位图时发生内存不足错误", e);
                offscreenBitmap = null;
                offscreenCanvas = null;
                return false;
            }
        }
        return true; // 位图已准备好
    }

    // 从历史数据重绘整个离屏位图（通常在位图尺寸改变后调用）
    private void redrawEntireBitmapFromHistory() {
        if (offscreenBitmap == null || offscreenCanvas == null || getWidth() <= 0 || getHeight() <= 0) return;

        offscreenCanvas.drawColor(viewBackgroundColor); // 首先用背景色清空画布

        int viewWidth = offscreenBitmap.getWidth();
        int viewHeight = offscreenBitmap.getHeight();

        // 计算实际可以绘制的历史条目数量
        int drawableHistoryCount = Math.min(spectrumHistory.size(), (int) (viewWidth / (float)spectrumBarWidth));
        if (drawableHistoryCount <= 0) return; // 没有可绘制的历史数据

        // 从最旧的可绘制历史条目开始绘制，直到最新的
        for (int i = 0; i < drawableHistoryCount; i++) {
            // 获取历史条目：最旧的可绘制条目在左侧，最新的在右侧。
            // spectrumHistory.size() - drawableHistoryCount 得到可绘制条目的起始索引。
            int historyIndex = spectrumHistory.size() - drawableHistoryCount + i;
            if (historyIndex < 0 || historyIndex >= spectrumHistory.size()) continue; // 安全检查

            float[] rawSpectrumData = spectrumHistory.get(historyIndex); // 获取原始历史数据
            float[] enhancedData = enhanceColumn(rawSpectrumData); // 对历史数据进行增强处理

            // 计算此列的 x 坐标
            // 最右侧列的 x 坐标是 viewWidth - spectrumBarWidth
            // historyIndex = (spectrumHistory.size() - 1) 对应最右侧的列
            // historyIndex = (spectrumHistory.size() - drawableHistoryCount) 对应
            // viewWidth - drawableHistoryCount * spectrumBarWidth 的位置
            float x = viewWidth - (drawableHistoryCount - i) * spectrumBarWidth;
            if (x + spectrumBarWidth <=0) continue; // 如果完全在屏幕左侧外部，则不绘制

            drawSpectrumColumnOnCanvas(offscreenCanvas, enhancedData, x, spectrumBarWidth, viewHeight, SPECTRUM_DATA_SIZE);
        }
    }

    // 在画布上绘制单列频谱（热力图的一列）
    // 接收的是增强后的数据 (0-1 范围)
    private void drawSpectrumColumnOnCanvas(Canvas canvas, float[] enhancedSpectrumData, float x, float barWidth,
                                            int canvasHeight, int dataPoints) {
        if (enhancedSpectrumData == null || enhancedSpectrumData.length != dataPoints || barWidth <= 0) return;

        float pointHeight = (float) canvasHeight / dataPoints; // 每个频谱数据点在垂直方向的高度

        for (int j = 0; j < dataPoints; j++) {
            // enhancedSpectrumData[j] 已经是归一化后的能量值 (0.0 到 1.0)
            float normalizedEnergy = enhancedSpectrumData[j];
            // 将归一化能量映射到颜色查找表的索引
            int colorIndex = (int) (normalizedEnergy * (LUT_SIZE - 1));
            spectrumPaint.setColor(lut[colorIndex]); // 设置画笔颜色

            // 绘制方向：低频 (索引 0) 在底部，高频在顶部
            // Y 轴坐标系中，(0,0) 在左上角，Y 值向下增加
            float yTop = canvasHeight - (j + 1) * pointHeight; // 当前频点矩形的顶部 y 坐标
            float yBottom = yTop + pointHeight;                // 当前频点矩形的底部 y 坐标

            canvas.drawRect(x, yTop, x + barWidth, yBottom, spectrumPaint); // 绘制小矩形
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) { // 视图尺寸改变时调用
        super.onSizeChanged(w, h, oldw, oldh);
        Log.d(TAG, "onSizeChanged: " + w + "x" + h);
        if (w > 0 && h > 0) { // 新的宽高有效
            if (spectrumBarWidth > 0) {
                maxHistorySizeBasedOnWidth = w / spectrumBarWidth + 2; // 根据新宽度更新历史记录容量 (+2作为缓冲)
            } else { // spectrumBarWidth 理论上应强制 >= 1
                maxHistorySizeBasedOnWidth = w + 2;
            }
            trimSpectrumHistory(); // 整理历史记录以适应新容量
            // 位图的重新创建和完整重绘由 ensureOffscreenBitmap 处理，
            // 它会在绘图操作之前或此处显式调用。
            if (ensureOffscreenBitmap()) {
                // redrawEntireBitmapFromHistory(); // ensureOffscreenBitmap 在位图是新的或尺寸改变时会调用它
            }
        } else { // 新的宽高无效
            releaseOffscreenBitmap(); // 释放位图
        }
        invalidate(); // 请求重绘以反映尺寸变化
    }

    // 整理频谱历史记录，移除超出 maxHistorySizeBasedOnWidth 的最旧条目
    private void trimSpectrumHistory() {
        while (spectrumHistory.size() > maxHistorySizeBasedOnWidth) {
            spectrumHistory.removeFirst(); // 移除队列头部的元素 (最旧的)
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // --- 核心修改：根据 show 标志位决定绘制内容 ---
        if (show) {
            // --- 状态为“显示”时，执行原来的绘制逻辑 ---
            if (offscreenBitmap == null || offscreenBitmap.isRecycled() || getWidth() <= 0 || getHeight() <= 0) {
                canvas.drawColor(viewBackgroundColor);
                return;
            }
            canvas.drawBitmap(offscreenBitmap, 0, 0, bitmapPaint);
            // --- 新增：在频谱图上层绘制信息文本 ---
            if (infoText != null && !infoText.isEmpty()) {
                // y 坐标是文字的基线位置，不是顶部。
                // 所以 y = margin + textSize 差不多能让文字的顶部位于 margin 处。
                float textX = infoTextMargin;
                float textY = infoTextMargin + infoTextPaint.getTextSize();
                canvas.drawText(infoText, textX, textY, infoTextPaint);
            }
        } else {
            // --- 状态为“不显示”时，绘制“已暂停”界面 ---
            // 1. 绘制白色背景
            canvas.drawColor(Color.WHITE);

            // 2. 绘制居中的提示文字
            int viewWidth = getWidth();
            int viewHeight = getHeight();
            // 水平居中：由于画笔已设置 TextAlign.CENTER，x 坐标直接设为视图宽度的一半
            float x = viewWidth / 2f;
            // 垂直居中：需要计算文字的高度来辅助定位
            pausedTextPaint.getTextBounds(PAUSED_TEXT, 0, PAUSED_TEXT.length(), textBounds);
            float y = viewHeight / 2f + textBounds.height() / 2f;

            canvas.drawText(PAUSED_TEXT, x, y, pausedTextPaint);
        }
    }

    // --- 新增：公共方法，用于在代码中设置信息文本 ---
    /**
     * 设置要在视图左上角显示的信息文本。
     * @param text 要显示的文本。如果为 null 或空字符串，则不显示任何文本。
     */
    public void setInfoText(String text) {
        this.infoText = text;
        invalidate(); // 请求重绘以立即更新文本
    }

    /**
     * 获取当前显示的信息文本。
     * @return 当前的文本。
     */
    public String getInfoText() {
        return this.infoText;
    }

    // 释放离屏位图及其画布资源
    private void releaseOffscreenBitmap() {
        if (offscreenBitmap != null) {
            if (!offscreenBitmap.isRecycled()) {
                offscreenBitmap.recycle(); // 回收位图内存
            }
            offscreenBitmap = null;
            offscreenCanvas = null;
            Log.d(TAG, "离屏位图已释放");
        }
    }

    @Override
    protected void onAttachedToWindow() { // 视图附加到窗口时调用
        super.onAttachedToWindow();
        // 可以在此处重新注册帧回调（如果需要），或者等待下一次 updateSpectrum 调用
    }

    @Override
    protected void onDetachedFromWindow() { // 视图从窗口分离时调用
        super.onDetachedFromWindow();
        if (frameCallbackRegistered) { // 如果已注册帧回调，则移除
            choreographer.removeFrameCallback(this);
            frameCallbackRegistered = false;
        }
        releaseOffscreenBitmap(); // 释放位图资源
        synchronized (dataLock) { // 清理待处理数据，防止内存泄漏或在重新附加时处理旧数据
            pendingSpectrumData = null;
            hasPendingData = false;
        }
        Log.d(TAG, "已从窗口分离，清理完成。");
    }

    // --- 公共配置方法 ---

    /**
     * 设置频谱条（热力图中的列）的宽度。
     * @param widthInPx 宽度，单位像素。对于热力图效果，理想值为1。
     */
    public void setSpectrumBarWidth(int widthInPx) {
        this.spectrumBarWidth = Math.max(1, widthInPx); // 宽度至少为1
        if (getWidth() > 0) { // 仅当视图有尺寸时继续
            maxHistorySizeBasedOnWidth = getWidth() / this.spectrumBarWidth + 2;
            trimSpectrumHistory();
            if (ensureOffscreenBitmap()) { // 确保位图存在
                redrawEntireBitmapFromHistory(); // 用新宽度重绘历史数据
            }
        }
        invalidate(); // 请求重绘
    }

    // setSpectrumBaseColor 方法已移除，因为现在使用固定的颜色渐变。
    // 如果需要可配置的颜色渐变，会是更复杂的改动。

    /**
     * 设置频谱图的背景颜色。
     * @param color ARGB 颜色值。
     */
    public void setSpectrumBackgroundColor(int color) {
        this.viewBackgroundColor = color;
        // 如果位图已存在，需要重绘，因为背景色是离屏位图的一部分。
        if (getWidth() > 0 && getHeight() > 0 && ensureOffscreenBitmap()) {
            redrawEntireBitmapFromHistory(); // 此方法会用新背景色清空并重绘内容
        }
        invalidate(); // 请求重绘
    }

    /**
     * 清空频谱图显示和历史数据。
     */
    public void clear() {
        if (frameCallbackRegistered) { // 停止任何待处理的帧回调
            choreographer.removeFrameCallback(this);
            frameCallbackRegistered = false;
        }
        synchronized (dataLock) { // 清空待处理数据
            pendingSpectrumData = null;
            hasPendingData = false;
        }
        spectrumHistory.clear(); // 清空历史记录
        if (offscreenCanvas != null) { // 如果画布存在，用背景色清空
            offscreenCanvas.drawColor(viewBackgroundColor);
        }
        invalidate(); // 请求重绘以显示清空后的状态
    }
}